// JavaScript Document
let requestAnimationFrame = window.requestAnimationFrame || function(callback) {
    window.setTimeout(callback, 1000 / 60)
};


let canvas = document.getElementsByTagName("canvas")[1];
let ctx2 = canvas.getContext("2d");
let maximumPossibleDistance;
let centerX;
let centerY;
let mousePositionX;
let mousePositionY;
let mouseElement;
let isRunning;

let lines = 0;
let objects = [];

let initAnimation = function(){


    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    maximumPossibleDistance = Math.round(Math.sqrt((canvas.width * canvas.width) + (canvas.height * canvas.height)));

    centerX = Math.floor(canvas.width / 2);
    centerY = Math.floor(canvas.height / 2);

    objects.length = 0;
    clearCanvas();
    createParticles();

};

window.addEventListener("resize", function(){initAnimation();},false);

let options = {

    particlesNumber: 100,
    initialSize: 3,
    moveLimit: 50,
    durationMin: 50,
    durationMax: 300,
    drawConnections: true,
    mouseInteractionDistance:150,
    mouseGravity:true,
    drawMouseConnections:true,
    red:255,
    green:0,
    blue:255,
    opacity:1,
    connectionRed:255,
    connectionGreen:255,
    connectionBlue:255,
    connectionOpacity:0.2,
    mouseConnectionRed:255,
    mouseConnectionGreen:255,
    mouseConnectionBlue:255,
    mouseConnectionOpacity:0.2

}

// ----------------------------------------------------
// Helper functions //
//-----------------------------------------------------

let getRandomBetween = function(a, b) {

    return Math.floor(Math.random() * b) + a;

};

let hitTest = function(object1, object2) {


    if ((object1.positionX < object2.positionX + object2.size) && (object1.positionX + object2.size > object2.positionX) &&
        (object1.positionY < object2.positionY + object2.size) && (object1.positionY > object2.positionY)) {

        return true;


    } else {

        return false;

    };


};

// Get distance between particles by using Pythagorean theorem

let getDistance = function(element1, element2) {


    let difX = Math.round(Math.abs(element1.positionX - element2.positionX));
    let difY = Math.round(Math.abs(element1.positionY - element2.positionY));

    return Math.round(Math.sqrt((difX * difX) + (difY * difY)));


};



// ----------------------------------------------------
// Particle constructor function //
//-----------------------------------------------------
function Particle(positionX, positionY, size, red, green, blue, opacity) {

    this.positionX = positionX;
    this.positionY = positionY;
    this.size = size;

    this.duration = getRandomBetween(options.durationMin, options.durationMax);
    this.limit = options.moveLimit
    this.timer = 0;

    this.red = red
    this.green = green
    this.blue = blue
    this.opacity = opacity


    this.color = "rgba(" + this.red + "," + this.green + "," + this.blue + ",+" + this.opacity + ")";

};

// ----------------------------------------------------
// Mouse Particle constructor function //
//-----------------------------------------------------
function MouseParticle(positionX, positionY, size, red, green, blue, opacity) {

    this.positionX = mousePositionX;
    this.positionY = mousePositionY;
    this.size = size;

    this.red = red
    this.green = green
    this.blue = blue
    this.opacity = opacity


    this.color = "rgba(" + this.red + "," + this.green + "," + this.blue + ",+" + this.opacity + ")";

};




Particle.prototype.animateTo = function(newX, newY) {

    let duration = this.duration;

    let animatePosition = function(newPosition, currentPosition) {

        if (newPosition > currentPosition) {

            let step = (newPosition - currentPosition) / duration;
            newPosition = currentPosition + step;

        } else {

            let step = (currentPosition - newPosition) / duration;
            newPosition = currentPosition - step;

        };

        return newPosition;

    }

    this.positionX = animatePosition(newX, this.positionX)
    this.positionY = animatePosition(newY, this.positionY)



    // generate new vector

    if (this.timer == this.duration) {

        this.calculateVector();
        this.timer = 0;

    } else {

        this.timer++;

    }


};

Particle.prototype.updateColor = function() {

    this.color = "rgba(" + this.red + "," + this.green + "," + this.blue + ",+" + this.opacity + ")";

};

Particle.prototype.calculateVector = function() {


    let distance
    let newPosition = {};
    let particle = this;

    let getCoordinates = function() {


        newPosition.positionX = getRandomBetween(0, window.innerWidth);
        newPosition.positionY = getRandomBetween(0, window.innerHeight);

        distance = getDistance(particle, newPosition);

    };

    while ((typeof distance === "undefined") || (distance > this.limit)) {

        getCoordinates();

    }


    this.vectorX = newPosition.positionX;
    this.vectorY = newPosition.positionY;


};


Particle.prototype.testInteraction = function() {

    if (!options.drawConnections) return;

    let closestElement;
    let distanceToClosestElement = maximumPossibleDistance;

    for (let x = 0; x < objects.length; x++) {

        let testedObject = objects[x];
        let distance = getDistance(this, testedObject)


        if ((distance < distanceToClosestElement) && (testedObject !== this)) {

            distanceToClosestElement = distance;
            closestElement = testedObject;

        }

        // Hittest

       /* if (hitTest(this, testedObject)) {

            let winner = true;

            if (this.size > testedObject.size) {

                let objToDelete = testedObject
                let objToSave = this


            } else if (this.size < testedObject.size) {

                let objToDelete = this
                let objToSave = testedObject

            } else {

                winner = false

            }


            if (winner) {

                let index = objects.indexOf(objToDelete)
                objects.splice(index, 1)
                objToSave.size++;



                if (objToSave.opacity <= 1) objToSave.opacity += 0.1;

                objToSave.updateColor();
            }

        }*/

        // Hittest end



    };

    if (closestElement) {

        ctx2.beginPath();
        ctx2.moveTo(this.positionX + this.size / 2, this.positionY + this.size / 2);
        ctx2.lineTo(closestElement.positionX + closestElement.size * 0.5, closestElement.positionY + closestElement.size * 0.5);
        ctx2.strokeStyle = "rgba(" + options.connectionRed + ","+ options.connectionGreen +","+ options.connectionBlue +"," + options.connectionOpacity + ")";
        ctx2.stroke();
        lines++
    }

};

MouseParticle.prototype.testInteraction = function() {

    if (options.mouseInteractionDistance === 0) return;

    let closestElements = []
    let distanceToClosestElement = maximumPossibleDistance;

    for (let x = 0; x < objects.length; x++) {

        let testedObject = objects[x];
        let distance = getDistance(this, testedObject)


        if ((distance < options.mouseInteractionDistance) && (testedObject !== this)) {


            closestElements.push(objects[x])

        }

    }


    for (let x = 0; x < closestElements.length; x++) {


        if (options.drawMouseConnections) {

            let element = closestElements[x]
            ctx2.beginPath();
            ctx2.moveTo(this.positionX, this.positionY);
            ctx2.lineTo(element.positionX + element.size * 0.5, element.positionY + element.size * 0.5);
            ctx2.strokeStyle = "rgba(" + options.mouseConnectionRed + ","+ options.mouseConnectionGreen +","+ options.mouseConnectionBlue +"," + options.mouseConnectionOpacity + ")";
            ctx2.stroke();
            lines++

        }

        if (options.mouseGravity) {

            closestElements[x].vectorX = this.positionX;
            closestElements[x].vectorY = this.positionY;

        }





    }


};

Particle.prototype.updateAnimation = function() {

    this.animateTo(this.vectorX, this.vectorY);
    this.testInteraction();
    ctx2.fillStyle = this.color;
    ctx2.fillRect(this.positionX, this.positionY, this.size, this.size);

};



MouseParticle.prototype.updateAnimation = function() {


    this.positionX = mousePositionX;
    this.positionY = mousePositionY;

    this.testInteraction();


};


let createParticles = function() {

    // create mouse particle
    mouseElement = new MouseParticle(0, 0, options.initialSize, 255, 255, 255,)


    for (let x = 0; x < options.particlesNumber; x++) {

        let randomX = Math.floor((Math.random() * window.innerWidth) + 1);
        let randomY = Math.floor((Math.random() * window.innerHeight) + 1);

        let particle = new Particle(randomX, randomY, options.initialSize, options.red, options.green, options.blue, options.opacity)
        particle.calculateVector()

        objects.push(particle)

    }



};


let updatePosition = function() {

    for (let x = 0; x < objects.length; x++) {

        objects[x].updateAnimation()

    }

    // handle mouse
    mouseElement.updateAnimation()




};


window.onmousemove = function(e){

    mousePositionX = e.clientX;
    mousePositionY = e.clientY;

}

let clearCanvas = function() {

    ctx2.clearRect(0, 0, window.innerWidth, window.innerHeight)


};

let stopAnimation = function(){


  window.cancelAnimationFrame(myAnimation)
  isRunning = false;

};



// ----------------------------------------------------
// FPS //
//-----------------------------------------------------
let lastCalledTime
let fps
let averageFps;
let averageFpsTemp = 0;
let averageFpsCounter = 0;


function requestFps() {

    if (!lastCalledTime) {

        lastCalledTime = Date.now();
        fps = 0;
        return;

    }



    delta = (new Date().getTime() - lastCalledTime) / 1000;
    lastCalledTime = Date.now();
    fps = Math.floor(1 / delta);

    averageFpsTemp = averageFpsTemp + fps;
    averageFpsCounter++;

    if ( averageFpsCounter === 5) {


        averageFps = Math.floor(averageFpsTemp / 5)
        averageFpsCounter = 0;
        averageFpsTemp = 0;
    }

    if (!averageFps) {

        return;

    } else if (averageFps < 10) {




    }



}





// ----------------------------------------------------
// Init! //
//-----------------------------------------------------

let loop = function() {

    clearCanvas();
    updatePosition();


    ctx2.fillStyle = "#fff";
    ctx2.fillText("FPS: " + fps + " lines: " + lines + " Average FPS: " + averageFps , 10, 20);
    lines = 0;


    myAnimation = requestAnimationFrame(loop);
    isRunning = true;
    requestFps();

};

initAnimation();
loop();